package data

import (
	"context"
	pb "digitalhuman/api/user/v1"
	"digitalhuman/internal/biz"
	"digitalhuman/internal/helper"
	"digitalhuman/models"
	"errors"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/golang-jwt/jwt/v4"
	"math/rand"
	"sync"
	"time"
)

type UserData struct {
	Data *Data
	log  log.Logger
	lock sync.Mutex
	dou  *DouYinData
	ds   *DsmData
}

func (u *UserData) Update(ctx context.Context, user *models.User) (*models.User, error) {
	u.Data.Db.Updates(user)

	return user, nil
}

func (u *UserData) FindByID(ctx context.Context, i int) (*models.User, error) {
	var user *models.User
	u.Data.Db.Where("id = ?", i).First(&user)
	if user == nil {

	}
	return user, nil
}

func (u *UserData) FindByUserName(ctx context.Context, name string) (*models.User, error) {
	var user *models.User
	u.Data.Db.Where("name = ?", name).First(&user)
	if user == nil {
		return nil, errors.New("user not exist")
	}
	return user, nil
}

// GenerateTokenAndSave 生成Token和存储
func (cd *UserData) GenerateTokenAndSave(c *models.User, secret string) (string, error) {
	// 创建一个新的 JWT 签名对象
	token := jwt.New(jwt.SigningMethodHS256)

	// 添加标准声明
	claims := token.Claims.(jwt.MapClaims)
	claims["exp"] = time.Now().Add(time.Hour * 24 * 10).Unix()

	// 添加自定义字段
	claims["user_id"] = c.ID
	claims["user_name"] = c.Name
	claims["user_role_type"] = c.RoleType

	// 签名token
	signedToken, err := token.SignedString([]byte(secret))
	if err != nil {
		return "", err
	}

	// 操作成功
	return signedToken, nil
}

func (u *UserData) UpdateUserDuration(uid int, duration int) (bool, int, error) {
	u.lock.Lock()
	defer u.lock.Unlock()

	var user models.User
	result := u.Data.Db.Where("id = ?", uid).First(&user)
	if result.Error != nil {
		return false, 0, result.Error
	}
	if user.Duration < duration {
		return false, 0, errors.New("duration is not enough")
	}
	newDuration := user.Duration - duration
	result = u.Data.Db.Model(&models.User{}).Where("id = ?", uid).Update("duration", newDuration)
	if result.Error != nil {
		return false, 0, result.Error
	}
	return true, user.Duration, nil
}

func (u *UserData) Register(ctx context.Context, p *pb.RegisterRequest) (*models.User, error) {
	// 用户注册
	if p.Password != p.PasswordConfirm {
		return nil, errors.New("password not equal")
	}
	user := &models.User{}
	// 验证一下用户名是否已经存在
	u.Data.Db.Where("name = ?", p.Username).First(&user)
	if user.Name != "" {
		return nil, errors.New("username already exist")
	}

	// 检测一下验证码是否正确
	code := u.Data.Rdb.Get(ctx, p.Phone+"_code")
	codeInt, err := code.Int()
	if err != nil {
		return nil, errors.New("code send error")
	}
	if int32(codeInt) != p.VerifyCode {
		return nil, errors.New("verify code error")
	}

	// 创建用户
	user.Name = p.Username
	passS, err := helper.HashPassword(p.Password)
	if err != nil {
		return nil, err
	}
	user.PassWord = passS
	user.RoleType = 1
	user.Tel = p.Phone
	u.Data.Db.Create(user)

	return user, nil
}

func (u *UserData) VerifyCode(ctx context.Context, p *pb.GetVerifyCodeReq) error {
	// 产生一个四位验证码
	rand.Seed(time.Now().UnixNano())
	verificationCode := rand.Intn(9000) + 1000
	phone := p.Telephone
	k := phone + "_code"
	u.Data.Rdb.Set(ctx, k, verificationCode, time.Minute*5)

	b := u.ds.SendMessage(phone, verificationCode)
	if !b {
		return errors.New("验证码发送失败")
	}

	return nil
}

func (u *UserData) GetDouYinShareUrl(ctx context.Context, req *pb.DouYinShareRequest) (string, error) {
	var section *models.StoryboardSection
	u.Data.Db.Model(&models.StoryboardSection{}).Where(&models.StoryboardSection{StoryboardId: int(req.StoryboardId)}).First(&section)
	var retUrl string
	if section.ID > 0 {
		urlStr, err := u.dou.GetDyShareSchema(section.VideoUrl)
		if err != nil {
			return "", err
		}
		retUrl = urlStr
	}

	return retUrl, nil
}

// NewUserBiz .
func NewUserBiz(data *Data, logger log.Logger, ds *DsmData, dou *DouYinData) biz.UserRepo {
	return &UserData{
		Data: data,
		log:  logger,
		ds:   ds,
		dou:  dou,
	}
}

func NewUserData(data *Data, logger log.Logger, dou *DouYinData, ds *DsmData) *UserData {
	return &UserData{
		Data: data,
		log:  logger,
		dou:  dou,
		ds:   ds,
	}
}
